package fasdk

import (
	"bytes"
	"errors"
	"gitee.com/bupt-zkjc/fabric2.2-interface/common/configtxgen/encoder"
	genesisconfig "gitee.com/bupt-zkjc/fabric2.2-interface/common/configtxgen/genesisconfig"
	"gitee.com/bupt-zkjc/fabric2.2-interface/common/configtxlator/update"
	"gitee.com/bupt-zkjc/fabric2.2-interface/constant"
	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric-protos-go/common"
	mspclient "gitee.com/bupt-zkjc/fabric-sdk-go/pkg/client/msp"
	"gitee.com/bupt-zkjc/fabric-sdk-go/pkg/client/resmgmt"
	"gitee.com/bupt-zkjc/fabric-sdk-go/pkg/common/providers/fab"
	"gitee.com/bupt-zkjc/fabric-sdk-go/pkg/common/providers/msp"
	"gitee.com/bupt-zkjc/fabric-sdk-go/pkg/fab/resource"
	"gitee.com/bupt-zkjc/fabric-sdk-go/pkg/fabsdk"
)

func (f *FabricClient) CreateChannel(channelPath string) error {
	mspClient, err := mspclient.New(f.sdk.Context(fabsdk.WithUser(f.OrgAdmin)), mspclient.WithOrg(f.Orgs[0]))
	if err != nil {
		logger.Error(err)
		return err
	}
	adminIdentity, err := mspClient.GetSigningIdentity(f.OrgAdmin)
	if err != nil {
		logger.Error(err)
		return err
	}
	req := resmgmt.SaveChannelRequest{
		ChannelID:         f.ChannelId,
		ChannelConfigPath: channelPath,
		SigningIdentities: []msp.SigningIdentity{adminIdentity},
	}
	txId, err := f.resmgmtClients[0].SaveChannel(req, f.retry, f.orderer)
	if err != nil {
		logger.Error(err)
		return err
	}
	logger.Info("%s", txId)
	return nil
}

func (f *FabricClient) UpdateChannel(anchorsTx []string) error {

	for i, c := range f.resmgmtClients {

		mspClient, err := mspclient.New(f.sdk.Context(), mspclient.WithOrg(f.Orgs[i]))
		if err != nil {
			logger.Error(err)
			return err
		}
		adminIdentity, err := mspClient.GetSigningIdentity(f.OrgAdmin)
		if err != nil {
			logger.Error(err)
			return err
		}
		req := resmgmt.SaveChannelRequest{
			ChannelID:         f.ChannelId,
			ChannelConfigPath: anchorsTx[i],
			SigningIdentities: []msp.SigningIdentity{adminIdentity},
		}
		txId, err := c.SaveChannel(req, f.retry, f.orderer)
		if err != nil {
			logger.Error(err)
			return err
		}
		logger.Info("%s", txId)
	}

	return nil
}

func (f *FabricClient) JoinChannel() error {

	for i, c := range f.resmgmtClients {
		err := c.JoinChannel(f.ChannelId, f.retry, f.orderer)
		if err != nil {
			logger.Errorf("Org peers failed to JoinChannel: %s", err)
			return err
		}
		logger.Info(f.Orgs[i], " join channel")
	}
	return nil

}
func (f *FabricClient) JoinNewOrgToChannel(txPath string) error {
	latestedCfg1, err := f.getChannelConfig()
	if err != nil {
		logger.Error(err)
		return err
	}
	//根据配置文件，生成新组织的配置
	newCfgGroup, err := f.genCfgGroupFromTx(txPath)
	if err != nil {
		logger.Error(err)
		return err
	}
	latestedCfg2, err := f.getChannelConfig()
	if err != nil {
		logger.Error(err)
		return err
	}
	//组装最新的通道配置
	latestedCfg2.ChannelGroup.Groups["Application"].Groups[f.Orgs[0]] = newCfgGroup

	cfgUpdate, err := f.getCompute(latestedCfg1, latestedCfg2)
	if err != nil {
		logger.Error(err)
		return err
	}
	envBytes, err := f.assembleEnvelop(cfgUpdate)
	if err != nil {
		logger.Error(err)
		return err
	}

	//通道中所有组织的签名
	originOrgs := len(f.resmgmtClients)
	var sigs []*common.ConfigSignature
	for i := 1; i < originOrgs; i++{
		ctx, err := f.sdk.Context(fabsdk.WithUser(f.OrgAdmin), fabsdk.WithOrg(f.Orgs[i]))()
		if err != nil {
			logger.Error("Generate ctx err", err)
			return err
		}
		sig, err := f.createConfigSignature(i,ctx,envBytes)
		sigs = append(sigs, sig)
		if err != nil {
			logger.Error(err)
			return err
		}
	}
	err = f.saveChannel(f.ChannelId, envBytes, sigs...)
	if err != nil {
		logger.Error(err)
		return err
	}
	return nil
}

func (f *FabricClient) RemoveExistedOrgFromChannel() error {
	latestedCfg1, err := f.getChannelConfig()
	if err != nil {
		logger.Error(err)
		return err
	}

	latestedCfg2, err := f.getChannelConfig()
	if err != nil {
		logger.Error(err)
		return err
	}
	//组装最新的通道配置
	delete(latestedCfg2.ChannelGroup.Groups["Application"].Groups, f.Orgs[0])

	cfgUpdate, err := f.getCompute(latestedCfg1, latestedCfg2)
	if err != nil {
		logger.Error(err)
		return err
	}
	envBytes, err := f.assembleEnvelop(cfgUpdate)
	if err != nil {
		logger.Error(err)
		return err
	}

	//通道中所有组织的签名
	originOrgs := len(f.resmgmtClients)
	var sigs []*common.ConfigSignature
	for i := 1; i < originOrgs; i++{
		ctx, err := f.sdk.Context(fabsdk.WithUser(f.OrgAdmin), fabsdk.WithOrg(f.Orgs[i]))()
		if err != nil {
			logger.Error("Generate ctx err", err)
			return err
		}
		sig, err := f.createConfigSignature(i,ctx,envBytes)
		sigs = append(sigs, sig)
		if err != nil {
			logger.Error(err)
			return err
		}
	}
	err = f.saveChannel(f.ChannelId, envBytes, sigs...)
	if err != nil {
		logger.Error(err)
		return err
	}
	return nil
}

func (f *FabricClient) JoinNewOrgToSystemChannel(txPath string) error {
	latestedCfg1, err :=f.getChannelConfig()
	if err != nil {
		logger.Error(err)
		return err
	}
	//根据配置文件，生成新组织的配置
	newCfgGroup, err := f.genCfgGroupFromTx(txPath)
	if err != nil {
		logger.Error(err)
		return err
	}
	latestedCfg2, err := f.getChannelConfig()
	if err != nil {
		logger.Error(err)
		return err
	}
	//组装最新的通道配置
	latestedCfg2.ChannelGroup.Groups["Consortiums"].Groups[f.ChainName].Groups[f.Orgs[0]] = newCfgGroup

	cfgUpdate, err := f.getCompute(latestedCfg1, latestedCfg2)
	if err != nil {
		logger.Error(err)
		return err
	}
	envBytes, err := f.assembleEnvelop(cfgUpdate)
	if err != nil {
		logger.Error(err)
		return err
	}

	//通道中所有组织的签名
	originOrgs := len(f.resmgmtClients)
	var sigs []*common.ConfigSignature
	for i := 1; i < originOrgs; i++{
		ctx, err := f.sdk.Context(fabsdk.WithUser(f.OrgAdmin), fabsdk.WithOrg(constant.OrdererOrg))()
		//fmt.Println("************************" + ctx.Identifier().ID)
		if err != nil {
			logger.Error("Generate ctx err", err)
			return err
		}
		sig, err := f.createConfigSignature(i,ctx,envBytes)
		sigs = append(sigs, sig)
		if err != nil {
			logger.Error(err)
			return err
		}
	}
	err = f.saveChannel(f.ChannelId, envBytes, sigs...)
	if err != nil {
		logger.Error(err)
		return err
	}
	return nil
}

func (f *FabricClient) RemoveExistedOrgFromSystemChannel() error {
	latestedCfg1, err :=f.getChannelConfig()
	if err != nil {
		logger.Error(err)
		return err
	}

	latestedCfg2, err := f.getChannelConfig()
	if err != nil {
		logger.Error(err)
		return err
	}
	//组装最新的通道配置
	delete(latestedCfg2.ChannelGroup.Groups["Consortiums"].Groups[f.ChainName].Groups, f.Orgs[0])

	cfgUpdate, err := f.getCompute(latestedCfg1, latestedCfg2)
	if err != nil {
		logger.Error(err)
		return err
	}
	envBytes, err := f.assembleEnvelop(cfgUpdate)
	if err != nil {
		logger.Error(err)
		return err
	}

	//通道中所有组织的签名
	originOrgs := len(f.resmgmtClients)
	var sigs []*common.ConfigSignature
	for i := 1; i < originOrgs; i++{
		ctx, err := f.sdk.Context(fabsdk.WithUser(f.OrgAdmin), fabsdk.WithOrg("ordererorg"))()
		//fmt.Println("************************" + ctx.Identifier().ID)
		if err != nil {
			logger.Error("Generate ctx err", err)
			return err
		}
		sig, err := f.createConfigSignature(i,ctx,envBytes)
		sigs = append(sigs, sig)
		if err != nil {
			logger.Error(err)
			return err
		}
	}
	err = f.saveChannel(f.ChannelId, envBytes, sigs...)
	if err != nil {
		logger.Error(err)
		return err
	}
	return nil
}

func (f *FabricClient) saveChannel(channelid string, channelCfg []byte, sigs... *common.ConfigSignature) error {
	resp, err := f.resmgmtClients[1].SaveChannel(resmgmt.SaveChannelRequest{
		ChannelID:     channelid,
		ChannelConfig: bytes.NewBuffer(channelCfg)}, resmgmt.WithConfigSignatures(sigs...))
	if err != nil {
		logger.Error(err)
		return err
	}
	logger.Info("add/update success!,resp txid is ", resp.TransactionID)
	return nil
}

func (f *FabricClient) genCfgGroupFromTx(txPath string) (*common.ConfigGroup, error) {
	if txPath == "" {
		return nil, errors.New("Txpath shoud not be empty")
	}
	var cfgG *common.ConfigGroup
	var topLevelConfig *genesisconfig.TopLevel
	topLevelConfig = genesisconfig.LoadTopLevel(txPath)
	var err error
	for _, org := range topLevelConfig.Organizations {
		if org.Name == f.Orgs[0] {
			cfgG, err = encoder.NewConsortiumOrgGroup(org)
			if err != nil {
				logger.Error("bad org definition for org :", err)
				return nil, err
			}
			return cfgG, nil
		}
	}
	return nil, errors.New("org gen ConfigGroup fail")
}

func (f *FabricClient) getChannelConfig() (*common.Config, error) {
	//f.resmgmtClients[0]是新加入组织，f.resmgmtClients[1]是第一个背书组织
	block, err := f.resmgmtClients[1].QueryConfigBlockFromOrderer(f.ChannelId)
	if err != nil {
		return nil, err
	}
	config, err := resource.ExtractConfigFromBlock(block)
	if err != nil {
		logger.Error("extractConfigFromBlock err:", err)
		return nil, err
	}
	return config, nil
}
func (f *FabricClient) getCompute(originalCfg, updateCfg *common.Config) (*common.ConfigUpdate, error) {
	computeUpdate, err := update.Compute(originalCfg, updateCfg)
	if err != nil {
		logger.Error(err)
		return nil, err
	}
	computeUpdate.ChannelId = f.ChannelId
	return computeUpdate, nil
}

// AssembleEnvelop 将更新配置扩展成Envelope，以便发送给fabric网络
func (f *FabricClient) assembleEnvelop(computeUpdate *common.ConfigUpdate) ([]byte, error) {
	// 组装channelHeader
	chHeader := new(common.ChannelHeader)
	chHeader.Type = 2
	chHeader.ChannelId = f.ChannelId
	//组装ConfigUpdateEnvelope
	cfgupdateBytes, err := proto.Marshal(computeUpdate)
	if err != nil {
	}
	cfgEnv := new(common.ConfigUpdateEnvelope)
	cfgEnv.ConfigUpdate = cfgupdateBytes
	//组装payload
	cfgEnvBytes, err := proto.Marshal(cfgEnv)
	if err != nil {
	}
	chHeaderBytes, err := proto.Marshal(chHeader)
	if err != nil {
	}
	payload := new(common.Payload)
	payload.Header = new(common.Header)
	payload.Data = cfgEnvBytes
	payload.Header.ChannelHeader = chHeaderBytes
	//组装envelope
	payloadBytes, err := proto.Marshal(payload)
	if err != nil {
	}
	envelope := new(common.Envelope)
	envelope.Payload = payloadBytes
	//将序列化的envelope返回
	return proto.Marshal(envelope)
}

func (f *FabricClient) createConfigSignature(index int, ctx fab.ClientContext,  envelop []byte) (*common.ConfigSignature, error) {
	return f.resmgmtClients[index].CreateConfigSignatureFromReader(ctx, bytes.NewReader(envelop))
}